/********************************************************************

Write Date: 2011/06/02
Author: Deng Nianchen
>>>> Implement OOPObject.

Modify Date: 2011/07/16
Author: Deng Nianchen
>>>> Fix bugs on handling operator new[].
>>>> Add comments to class OOPObjectCreateInfo
>>>> Implement copy constructor and assignment operator

Modify Date: 2012/12/01
Author: Deng Nianchen
>>>> Make OOPObject thread-safe.
>>>> Reimplement array creating operation.

Modify Date: 2015/04/14
Author: Apple
>>>> Rename OOPObject to Object.
>>>> Reorganize code fragments and simplify implementation of creation.
>>>> Remove the support of operator new[] and operator delete[].
>>>> Update the style of documents and codes.

Modify Date: 2015/04/20
Author: Apple
>>>> Add friend relationship to ObjectRefCountHandler.
>>>> Add a simple type check function isObject().

Modify Date: 2015/04/16
Author: Apple
>>>> Add const to the paremeter of copy constructor and copy assignment.

Modify Date: 2015/04/21
Author: Apple
>>>> Use C++11 new feature 'deleted function' to forbid the access to the 
     unsupported functions.
>>>> Correct _refNum initialization

Modify Date: 2015/04/26
Author: Apple
>>>> Mark Object(ResourceLoader&) and saveTo(ResourceSaver&) as protected. 
     Rename saveTo(ResourceSaver&) to __saveTo(ResourceSaver&).
>>>> Add documents for macros and struct Object::Meta.
>>>> Add const to the paremeter of copy constructor and copy assignment.

Modify Date: 2015/05/05
Author: Apple
>>>> Rename macros OOP_DEC_META and OOP_DEF_META(_TPL), add macros 
     BEGIN_OOP_CLASS(_TPL), END_OOP_CLASS.
>>>> Remove the use of constructor Object(ResourceLoader&), change load and save
     function to new public functions read(ResourceLoader&) and
	 write(ResourceSaver&).

Modify Date: 2015/05/07
Author: Apple
>>>> Add isType<T,T2>(ptr) for convenient type check.
>>>> Add const qualifier to functions needed.
>>>> Complete the document.

Modify Date: 2015/08/07
Author: Apple
>>>> Add field _name to Object.
>>>> Add field _syncStatus to Object.
>>>> Change the loader to creator in Object::Meta and remove the parameters.
>>>> Change the __loadFrom__ to __createObject__ in macro definitions.

Modify Date: 2015/08/13
Author: Apple
>>>> Change the operator new add a parameter to specify the Object's name.

Modify Date: 2015/08/17
Author: Apple
>>>> Change the implementation of operator new to clean the ResourceManager when 
     the memory is full.
>>>> Delete _isOnHeap field and related methods.
>>>> Delete copy constructor and assignment operator.
>>>> Make method read & write pure virtual.
>>>> Change the typedef of const pointer in macro BEGIN_OOP_CLASS and 
     BEGIN_OOP_CLASS_TPL
>>>> Add typedef CP_Object

*********************************************************************/
#pragma once
#include "ooputils_global.h"
#include <QMutex>
#include "CommonExceptions.h"



/**
 * Macro OOP_DEC_COMMON provides the fragment of meta-related declarations. Add 
 * the macro in your inherhited classes to gain the full function of Object IO
 * and other functions related to class meta.
 */
#define OOP_DEC_COMMON \
	friend class ::OOP::ResourceSaver; \
	friend class ::OOP::ResourceLoader; \
	static ::OOP::Object::Meta __objectMeta__; \
	static ::OOP::Object::Meta __createMeta__ (); \
	static ::OOP::P_Object __createObject__ (); \
	virtual void __fakeFunc__ () { ::OOP::Object::Meta a = __objectMeta__; }



/**
 * Macro OOP_DEF_COMMON is the correspond definition fragment of OOP_DEC_COMMON. 
 * Add the macro in your implementation file of your inherited classes.
 */
#define OOP_DEF_COMMON(__TYPE__) \
	::OOP::Object::Meta __TYPE__::__objectMeta__ = __TYPE__::__createMeta__ (); \
	::OOP::Object::Meta __TYPE__::__createMeta__ () { \
		::OOP::Object::Meta m = { \
			typeid(__TYPE__).name (), \
			&__TYPE__::__createObject__ \
		}; \
		::OOP::ClassMetaManager::ref ().registerClass (m); \
		return m; \
	} \
	::OOP::P_Object __TYPE__::__createObject__ () { return NEWOBJ_NONAME (__TYPE__); } \
	::OOP::ResourceLoader& operator>> (::OOP::ResourceLoader& r_loader, __TYPE__&) { OOPTHROW (::OOP::InvalidInvokationException ()); return r_loader; } \
	::OOP::ResourceSaver& operator<< (::OOP::ResourceSaver& r_saver, const __TYPE__&) { OOPTHROW (::OOP::InvalidInvokationException ()); return r_saver; }



/**
 * Macro OOP_DEF_COMMON_TPL is the correspond definition fragment of 
 * OOP_DEC_COMMON for your template classes. Add the macro after the declaration 
 * of your template classes. Below is an example for it.
 * The macro of Class Example< T1, T2 > should be written as:
 *   OOP_DEF_COMMON_TPL (TPL_CLASS (Example, T1, T2), class T1, class T2)
 */
#define OOP_DEF_COMMON_TPL(__TYPE__, ...) \
	template< __VA_ARGS__ > \
	::OOP::Object::Meta __TYPE__::__objectMeta__ = __TYPE__::__createMeta__ (); \
	template< __VA_ARGS__ > \
	::OOP::Object::Meta __TYPE__::__createMeta__ () { \
		::OOP::Object::Meta m = { \
			typeid(__TYPE__).name (), \
			&__TYPE__::__createObject__ \
		}; \
		::OOP::ClassMetaManager::ref ().registerClass (m); \
		return m; \
	} \
	template< __VA_ARGS__ > \
	::OOP::P_Object __TYPE__::__createObject__ () { return NEWOBJ_NONAME (__TYPE__); } \
	::OOP::ResourceLoader& operator>> (::OOP::ResourceLoader&, __TYPE__&) { OOPTHROW (::OOP::InvalidInvokationException ()); } \
	::OOP::ResourceSaver& operator<< (::OOP::ResourceSaver&, const __TYPE__&) { OOPTHROW (::OOP::InvalidInvokationException ()); }



/**
 * Macro BEGIN_OOP_CLASS gives all necessary codes to declare an Object class. 
 * It also defines an alias to the pointer of your class, named as P_xxx.
 */
#define BEGIN_OOP_CLASS(__TYPE__, ...) \
	class __TYPE__; \
	typedef ::OOP::Ptr< __TYPE__ > P_##__TYPE__; \
	typedef ::OOP::Ptr< const __TYPE__ > CP_##__TYPE__; \
	::OOP::ResourceLoader& operator>> (::OOP::ResourceLoader&, __TYPE__&); \
	::OOP::ResourceSaver& operator<< (::OOP::ResourceSaver&, const __TYPE__&); \
	class __VA_ARGS__ __TYPE__ : public ::OOP::Object { \
		OOP_DEC_COMMON;



/**
 * Macro BEGIN_OOP_CLASS_TPL is to handle the special synatax for the template 
 * class. 
 * The macro of Class Example< T1, T2 > should be written as:
 *   BEGIN_OOP_CLASS_TPL (TPL_CLASS (Example, T1, T2), Example, class T1, class T2)
 */
#define BEGIN_OOP_CLASS_TPL(__TYPE_FULL__, __TYPE__, ...) \
	template< __VA_ARGS__ > \
	class __TYPE__; \
	template< __VA_ARGS__ > \
	using P_##__TYPE__ = ::OOP::Ptr< __TYPE_FULL__ >; \
	template< __VA_ARGS__ > \
	using CP_##__TYPE__ = ::OOP::Ptr< const __TYPE_FULL__ >; \
	::OOP::ResourceLoader& operator>> (::OOP::ResourceLoader&, __TYPE_FULL__&); \
	::OOP::ResourceSaver& operator<< (::OOP::ResourceSaver&, const __TYPE_FULL__&); \
	template< __VA_ARGS__ > \
	class __TYPE__ : public ::OOP::Object { \
		OOP_DEC_COMMON;



/**
 * Macro END_OOP_CLASS gives the end of your class declaration.
 */
#define END_OOP_CLASS \
	};



_NS_OOP_BEGIN



class ResourceLoader;
class ResourceSaver;
template< class T > class Ptr;



/**
 * Class Object is the basic class in the framework. It provides support for 
 * strong smart pointer (class Ptr) by recording reference number and whether 
 * it's on the heap or stack. Thus almost all limits in using traditional smart 
 * pointer are removed if you use Ptr with the object of Object.
 * I recommend you to let your classes inherit Object to gain the advantages of 
 * Ptr. It will let you feel free with C/C++ pointer.
 * Note that creation of Object array (created by operator new[]) is not 
 * supported from now on. Use Ptr array instead.
 *
 * Thread-Safe: true.
 *
 * @remarks 2015/04/14, Apple
 */
class Object
{
public:

	/**
	 * Struct Object::Meta records the meta of a class inherited from class 
	 * Object. The meta includes the name and the loader function. The meta will 
	 * be stored in the class as well as in the global ClassMetaManager.
	 *
	 * @remarks 2015/04/26, Apple
	 */
	struct Meta {
		QString className;
		Ptr< Object > (*creator) ();
	};

	enum SyncStatus {
		NEW, MODIFIED, NOT_CHANGED
	};

private:

	friend class ObjectRef;
	friend class ResourceLoader;
	friend class ResourceSaver;

public:

	/**
	 * Initialize the reference count and is-on-heap indicator.
	 */
	Object ();

	/**
	 * Destroy the Object.
	 */
	virtual ~Object ();

	/**
	 * Overloading the operator new to record that the object is on the heap.
	 *
	 * @param size - size in bytes to allocate
	 * @return the pointer of allocated space
	 */
	static void* operator new (size_t size);
	static void* operator new (size_t size, const QString& r_name);

	/**
	 * Corresponding operator delete.
	 */
	static void operator delete (void* ptr, const QString& r_name);
	static void operator delete (void* ptr);

	/**
	 * The getter and setter of field _name
	 */
	const QString& name () const;
	void name (const QString& r_val);

	/**
	 * Get the reference count of the object
	 *
	 * @return the referenct count
	 */
	int getRefNum () const;

	/**
	 * Check whether class T is (inherited from) Object or not
	 *
	 * @return whether class T is (inherited from) Object or not
	 */
	template< class T >
	static bool isObject ();

	/**
	 * Check whether a pointer's type is (inherited from) Object or not
	 * This function is called if the parameter is not an Object pointer. So the 
	 * function returns false.
	 *
	 * @param ptr - the pointer to check
	 * @return false
	 */
	static bool isObject (void* ptr);

	/**
	 * Check whether a pointer's type is (inherited from) Object or not
	 * This function is called if the parameter is an Object pointer. So the 
	 * function returns true.
	 *
	 * @param ptr - the pointer to check
	 * @return true
	 */
	static bool isObject (Object* ptr);

	/**
	 * Check whether a T2 pointer's target is actually type T.
	 *
	 * @param ptr - the pointer to check
	 * @return whether a T2 pointer's target is actually type T
	 */
	template< class T, class T2 >
	static bool isType (T2* ptr);

	/**
	 * Read from external resource using a ResourceLoader.
	 *
	 * @param r_loader - the ResourceLoader
	 */
	virtual void read (ResourceLoader& r_loader) = 0;

	/**
	 * Write to external resources using a ResourceSaver.
	 *
	 * @param r_saver - the ResourceSaver
	 */
	virtual void write (ResourceSaver& r_saver) const = 0;

protected:

	void __contentModified ();

private:

	/* The name, also used as storage file name */
	QString _name;
	/* The status variable to show whether the object is newly created,
	not modified after loaded, or modified after loaded */
	SyncStatus _syncStatus;
	/* The reference count */
	int _refNum;
	/* The mutex to prevent read / write conflict to _refNum */
	QMutex _mutex;

	/**
	 * Increase the reference count by one
	 */
	void _incrRef ();

	/**
	 * Decrease the referene count by one
	 */
	void _decrRef ();

public:

	/* Functions below are forbidden */
	static void* operator new[] (size_t size) = delete;
	static void* operator new[] (size_t size, size_t elemNum, size_t elemSize) = delete;
	static void operator delete[] (void* ptr) { OOPTHROW (InvalidInvokationException ()); }
	static void operator delete[] (void* ptr, size_t elemNum, size_t elemSize) { OOPTHROW (InvalidInvokationException ()); }

};
typedef Ptr< Object > P_Object;
typedef Ptr< const Object > CP_Object;



_NS_OOP_END



#include "Object.hpp"